#include "World.h"
float PI = 3.14159265359;
void World::LoadPhysics()
{
p_world = new BulletWorld();
p_world->set_gravity(0, 0, -9.811);
this->DrawDebug(false);

	/*BulletPlaneShape *plane = new BulletPlaneShape(LVector3f(0,0,1),0);
	BulletRigidBodyNode *rigid = new BulletRigidBodyNode();
	rigid->add_shape(plane);
	rigid->set_friction(0.8);
	p_world->attach_rigid_body(rigid);*/
}
void World::DrawDebug(bool state)
{
p_debug = new BulletDebugNode("Debug");
p_debug->show_wireframe(true);
 
p_debug_n = window->get_render().attach_new_node(p_debug);
if(state)
	p_debug_n.show();
 
p_world->set_debug_node(p_debug);
}
void World::LoadMap(std::string name)
{
	window->get_render().set_shader_auto();
	m_mesh = window->load_model(window->get_render(), "data/maps/"+name+".bam");
	m_mesh.reparent_to(window->get_render());
	NodePathCollection node = m_mesh.find_all_matches("**/+GeomNode");
	for(int n=0;n<node.get_num_paths();n++)//parse map
	{
		NodePath n_node = node.get_path(n);
		std::string name = node.get_path(n).get_name();
		if (name.compare(0, 6, "Plight") == 0)//pointlight
		{
			m_lights.push_back(n_node);
			n_node.hide();
			n_node.reparent_to(window->get_render());
			continue;
		}
		if (name.compare(0, 4, "Cube") == 0)//box
		{
			BulletRigidBodyNode* t_body = this->PhysicsCube(1,1,1,50000);
			NodePath box = window->get_render().attach_new_node(t_body);
			box.set_pos(n_node.get_pos());
			n_node.set_pos(0,0,0);
			box.attach_new_node(n_node.get_node(0));
			p_world->attach_rigid_body(t_body);
			m_objects.push_back(box);
			n_node.remove_node();
			continue;
		}
		if (name.compare(0, 6, "Sphere") == 0)//sphere
		{
			BulletRigidBodyNode* t_body = this->PhysicsSphere(1,5);
			NodePath box = window->get_render().attach_new_node(t_body);
			box.set_pos(n_node.get_pos());
			n_node.set_pos(0,0,0);
			box.attach_new_node(n_node.get_node(0));
			p_world->attach_rigid_body(t_body);
			m_objects.push_back(box);
			n_node.remove_node();
			continue;
		}
		if (name.compare(0, 4, "Lamp") == 0)//pointlight
		{
			m_lights.push_back(n_node);
			n_node.hide();
			n_node.reparent_to(window->get_render());
			continue;
		}
		if (name.compare(0, 5, "Water") == 0)//pointlight
		{
			this->CreateWater(n_node);
			water = window->get_render().attach_new_node("water");
			n_node.reparent_to(water);
			continue;
		}
		if (name.compare(0, 4, "Mesh") == 0)//Mesh
		{
			GeomNode *g = DCAST(GeomNode,n_node.get_node(0));
			BulletRigidBodyNode* t_body = this->PhysicsTriangleMesh(g);
			nout << "Body " << n_node.get_pos() << endl;
			p_world->attach_rigid_body(t_body);
			t_body->set_transform(n_node.get_transform());
			continue;
		}
	}
	m_mesh.set_shader_input("offset",0,0,0,0);
	m_mesh.set_shader_input("scale",10,10,10,10);
	m_mesh.set_shader_input("tex0",TexturePool::load_texture("data/textures/Grass.jpg"));
	m_mesh.set_shader_input("tex1",TexturePool::load_texture("data/textures/Rocks.jpg"));
	m_mesh.set_shader_input("tex2",TexturePool::load_texture("data/textures/Sand.jpg"));
	m_mesh.set_shader_input("alpha1",TexturePool::load_texture("data/textures/Alpha.png"));
	m_mesh.set_shader_input("camera",LPoint3(0,0,0));
	PT(Shader) myShader = Shader::load("data/shaders/Terrain.cg");
	//m_mesh.set_shader(myShader);
	m_mesh.set_texture(TexturePool::load_texture("data/textures/Terrain.png"),1);
	m_mesh.set_light_off();

	this->CreateLights();
	this->CreateSkyBox();
}
bool World::Update()
{
	p_world->do_physics(ClockObject::get_global_clock()->get_dt(), 20, 1.0 / 60.0);
	window->get_render().set_shader_input("camera",window->get_camera_group().get_pos(window->get_render()));
	window->get_render().set_shader_input("reflection",w_texture);
	float time = ClockObject::get_global_clock()->get_frame_time();
	LVector2f w_time = LVector2f(time,time);
	LVector2f w_head = LVector2f((this->player->heading+180)*(PI/180),player->heading);
	window->get_render().set_shader_input("time",time);
	if(!this->water_camera.is_empty())
	{
		this->water.set_shader_input("time",w_time,1);
		this->water.set_shader_input("heading",w_head,1);
		LMatrix4 mat = window->get_camera_group().get_mat(this->w_plane_node);
		mat *= w_plane.get_reflection_mat();
		water_camera.set_mat(mat);
	}
	return true;
}
#include "meshDrawer.h"
void World::CreateLights()
{
	MeshDrawer m = MeshDrawer();
	m.set_budget(1000);
	NodePath n = m.get_root();
	n.reparent_to(window->get_render());
	AmbientLight *alight = new AmbientLight("a_light");
	alight->set_color(LColor(0.2, 0.2, 0.2, 1));
	NodePath alnp = window->get_render().attach_new_node(alight);
	window->get_render().set_light(alnp);
	//DirectionalLight *dlight = new DirectionalLight("a_light");
	//NodePath dlnp = window->get_render().attach_new_node(dlight);
	//window->get_render().set_light(dlnp);
	for(int l=0;l<m_lights.size();l++)
	{
		PointerTo<PointLight> light = new PointLight("light");
		light->set_color(LVecBase4f(.7,.7,.7,1));
		NodePath lightN = window->get_render().attach_new_node(light);
		lightN.set_pos(m_lights[l].get_pos());
		window->get_render().set_light(lightN);
	}
}
BulletRigidBodyNode *World::PhysicsCube(float x,float y,float z,float weight)
{
	BulletBoxShape *box_shape = new BulletBoxShape(LVector3f(x, y, z));
	BulletRigidBodyNode *rigid = new BulletRigidBodyNode();
	rigid->add_shape(box_shape);
	rigid->set_mass(weight);
	return rigid;
}
BulletRigidBodyNode *World::PhysicsSphere(float r,float weight)
{
	BulletSphereShape *sphere_shape = new BulletSphereShape(r);
	BulletRigidBodyNode *rigid = new BulletRigidBodyNode();
	rigid->add_shape(sphere_shape);
	rigid->set_mass(weight);
	return rigid;
}
BulletRigidBodyNode *World::PhysicsTriangleMesh(GeomNode *n)
{
	BulletTriangleMesh *mesh = new BulletTriangleMesh();
	for(int i=0;i<n->get_num_geoms();i++)
	{
		mesh->add_geom(n->get_geom(i));
	}
	BulletTriangleMeshShape *mesh_shape = new BulletTriangleMeshShape(mesh,false);
	BulletRigidBodyNode *rigid = new BulletRigidBodyNode();
	rigid->add_shape(mesh_shape);
	rigid->get_object()->setCollisionFlags(btCollisionObject::CF_DISABLE_VISUALIZE_OBJECT);
	return rigid;
}
void World::CreateSkyBox()
{
	this->sky_box = window->load_model(window->get_render(),"data/models/skybox.egg");
		this->sky_box.reparent_to(window->get_render());
        this->sky_box.set_scale(1000);
        this->sky_box.set_bin("background", 0);
        this->sky_box.set_depth_write(0);
        this->sky_box.set_light_off();
}
void World::CreateWater(NodePath n)
{
	int res = 1;
	if(this->water_camera.is_empty()){
		printf("New Buffer \n");
	GraphicsOutput* w_buffer = window->get_graphics_output()->make_texture_buffer("Water",512*res,512*res);
	w_buffer->set_sort(-100);
	w_buffer->set_clear_color_active(true);
	w_buffer->set_clear_color(window->get_graphics_window()->get_clear_color());
	water_camera = window->make_camera();
	water_camera.reparent_to(window->get_render());
	DisplayRegion *region = w_buffer->make_display_region();
	region->set_camera(water_camera);
	DCAST(Camera,water_camera.node())->set_lens(window->get_camera(0)->get_lens());
	DCAST(Camera,water_camera.node())->set_camera_mask(BitMask32::bit(1));
	w_texture = w_buffer->get_texture();
	w_texture->set_wrap_u(Texture::WM_clamp);
	w_texture->set_wrap_v(Texture::WM_clamp);
	w_texture->set_minfilter(Texture::FT_linear_mipmap_linear);
	w_plane = LPlanef(LVector3f(0,0,1),LPoint3f(0,0,0));
	w_plane_node = window->get_render().attach_new_node(new PlaneNode("water",w_plane));
	NodePath tmpnp = NodePath("StateInitializer");
	tmpnp.set_clip_plane(w_plane_node);
	tmpnp.set_attrib(CullFaceAttrib::make_reverse());
	DCAST(Camera,water_camera.node())->set_initial_state(tmpnp.get_state());
	}
	n.hide(BitMask32::bit(1));
	n.set_transparency(TransparencyAttrib::M_alpha);
	n.set_shader_input("offset",0,0,0,0);
	n.set_shader_input("scale",30,30,10,10);
	n.set_shader_input("tex0",TexturePool::load_texture("data/textures/w_bump.jpg"));
	n.set_shader_input("wave",TexturePool::load_texture("data/textures/w_wave.jpg"));
	n.set_shader_input("camera",LPoint3(0,0,0));
	n.set_shader_input("Wcam", water_camera);
	n.set_shader_input("time",0.0f);
	n.set_shader_input("heading",0.0f);
	PT(Shader) waterShader = Shader::load("data/shaders/Water.cg");
	n.set_shader(waterShader);
}